function RepositoryCache(){
    this._cache = {};   
}

RepositoryCache.prototype = {
    cacheMonth: function(yearMonth, monthModel) {
        this._cache[yearMonth.toString()] = monthModel;
    },

    getMonth: function(yearMonth) {
        return this._cache[yearMonth.toString()];
    },

    clearMonth: function(yearMonth) {
        this._cache[yearMonth.toString()] = null;
    },

    hasCacheForMonth: function(yearMonth) {
        return !!this._cache[yearMonth.toString()];
    }
};

function CalendarRepository() {
    this._proxy = new CachedDtoProxy(new DtoProxy());
    this._cache = new RepositoryCache();
    this._notetaskRepository = new pwc.NoteTaskRepository();
    this._calendarNoteCache = new pwc.CalendarNoteCache();
}

CalendarRepository.prototype = {
    preloadCache: function(year, yearAndTravel) {
        this._proxy.putMonthAndTravelsToCache(year, DtoConverter.toYearAndTravelDto(yearAndTravel));
    },
    preloadYear: function(year) {
        this._proxy.preloadYear(year);
    },

    flushMonths: function() {
        this._proxy.flushMonths();
    },

    getMonthsInYear: function(year) {
        var me = this;
        var monthsInYear = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
        return monthsInYear.select(function(month) {
            return me.getMonthByYearMonth(new YearMonth(year, month));
        });
    },
    getMonthByYearMonth: function(yearMonth) {
        var month = this._getMonthByYearMonth(yearMonth);
        var prevMonth = this._getMonthByYearMonth(yearMonth.addMonths(-1));
        if (month.updateCompletenessInfo(prevMonth)) {
            this.markAsChanged([month]);
        };
        return month;
    },
    _getMonthByYearMonth: function(yearMonth) {
        if (!this._cache.hasCacheForMonth(yearMonth)) {
            var monthDto = this._proxy.getMonthByYearMonth(yearMonth);
            var travelSteps = this.getTravelSteps(yearMonth.year());
            var calendarNotes = this._getCalendarNotes(yearMonth.year());
            var monthModel = new Month(yearMonth.year(), yearMonth.month(), travelSteps, calendarNotes, monthDto);
            this._cache.cacheMonth(yearMonth, monthModel);
        }
        return this._cache.getMonth(yearMonth);
    },

    _getCalendarNotes: function(year) {
        if (!this._calendarNoteCache.getNotesByYear(year)) {
            var calendarNotes = this._notetaskRepository.getCalendarNotesByYear(year).select(function(note) {
                return new CalendarNote(note);
            });
            this._calendarNoteCache.setNotesByYear(year, calendarNotes);
        }
        return this._calendarNoteCache.getNotesByYear(year);
    },

    getNoteByDate: function(date) {
        return this._calendarNoteCache.getNoteByDate(date);
    },

    updateNoteCache: function(calendarNote) {
        this._calendarNoteCache.updateNote(calendarNote);
        this._cache.clearMonth(YearMonth.fromDate(calendarNote.getDate()));
    },

    markAsChanged: function(months) {
        var me = this;
        var dtos = months.select(this._toDto);
        this._proxy.markAsChanged(dtos);
        months.foreach(function(month) {
            me._cache.clearMonth(month.getYearMonth());
        });
    },

    _toDto: function(domainModel) {
        return domainModel.toDto();
    },

    saveTravelSteps: function(travelSteps) {
        var allTravelSteps = [].concat(travelSteps.add || []).concat(travelSteps.remove || []);
        this._clearMonthDomainCacheFor(allTravelSteps);
        var travelStepDtosToAdd = travelSteps.add ? travelSteps.add.select(this._toDto) : [];
        var travelStepsToRemove = travelSteps.remove ? travelSteps.remove.select(function(travelStep) {
            return travelStep.getId();
        }) : [];

        if (travelStepDtosToAdd.length > 0 || travelStepsToRemove.length > 0) {
            this._proxy.saveTravelSteps(travelStepDtosToAdd, travelStepsToRemove);
        }

    }
    ,

    _clearMonthDomainCacheFor: function(travelSteps) {
        var me = this;
        var travelStepDateRange = TravelStep.getDateRange(travelSteps);
        if (travelStepDateRange !== null) {
            var yearMonths = this._calculateApplicableMonths(travelStepDateRange);
            yearMonths.foreach(function(yearMonth) {
                me._cache.clearMonth(yearMonth);
            });
        }
    },

    getTravelSteps: function(year) {
        var dto = this._proxy.getTravelSteps(year);
        var travelSteps = dto.select(function(travelStepDto) {
            return new TravelStep(travelStepDto);
        });
        travelSteps = TravelStep.sortedTravelSteps(travelSteps);
        return travelSteps;
    },

    getTravelStepsInRange: function(startYear, endYear) {
        var travelSteps = [];
        for (var year = startYear; year <= endYear; year++) {
            var travelStepsForYear = this.getTravelSteps(year);
            travelStepsForYear.foreach(function(travelStep) {
                travelSteps.uniquePush(travelStep);
            });
        }
        return travelSteps;
    },

    getTravelStepById: function(id) {
        return new TravelStep(this._proxy.getTravelStepById(id));
    },

    getApplicableMonths: function(dateRange) {
        var me = this;
        var applicableMonths = this._calculateApplicableMonths(dateRange);
        var months = applicableMonths.select(function(yearMonth) {
            return me.getMonthByYearMonth(yearMonth);
        });
        return months;
    },

    getSortedApplicableMonths: function(dateRange, isAsc) {
        return this.getApplicableMonths(dateRange).sort(function(month1, month2) {
            return isAsc ? month1.getMonth() - month2.getMonth() : month2.getMonth() - month1.getMonth();
        });
    },

    _calculateApplicableMonths: function(dateRange) {
        var startYearMonth = dateRange.start.toYearMonth();
        var endYearMonth = dateRange.end.toYearMonth();
        var applicableMonths = [];
        while (!startYearMonth.isLaterThan(endYearMonth)) {
            applicableMonths.push(startYearMonth);
            startYearMonth = startYearMonth.addMonths(1);
        }
        return applicableMonths;
    }
};
